import re
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# 设置matplotlib以正确显示中文字符
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题


def parse_backtest_log(file_path):
    """
    解析回测日志文件，提取每个已平仓位的开仓波动率和最终盈亏。

    Args:
        file_path (str): 日志文件的路径。

    Returns:
        pandas.DataFrame: 包含'volatility'和'pnl'列的数据框。
    """
    try:
        with open(file_path, 'r', encoding='UTF-8') as f:
            content = f.read()
    except FileNotFoundError:
        print(f"错误：找不到文件 {file_path}")
        return None
    except Exception as e:
        print(f"读取文件时出错: {e}")
        return None

    # 使用"========="作为分隔符来切分每个交易记录块
    blocks = content.split('=========\n')

    open_positions = {}  # 用于存储开仓信息 {仓位ID: 波动率}
    completed_trades = []  # 用于存储已完成的交易 {'volatility': x, 'pnl': y}

    # 正则表达式模式
    id_pattern = re.compile(r"├─ 仓位ID : ([\w-]+)")
    open_volatility_pattern = re.compile(r"├─ 开仓原因: 波动率: ([\d.]+)")
    pnl_pattern = re.compile(r"├─ 本笔盈亏: ([-\d.]+)")

    for block in blocks:
        id_match = id_pattern.search(block)
        if not id_match:
            continue

        position_id = id_match.group(1)

        # 判断是开仓还是平仓
        if "[开仓做" in block:
            vol_match = open_volatility_pattern.search(block)
            if vol_match:
                volatility = float(vol_match.group(1))
                open_positions[position_id] = volatility

        elif "[平仓" in block or "[止损" in block:
            pnl_match = pnl_pattern.search(block)
            if pnl_match and position_id in open_positions:
                pnl = float(pnl_match.group(1))
                volatility = open_positions.pop(position_id)  # 匹配后从开仓字典中移除

                completed_trades.append({
                    'volatility': volatility,
                    'pnl': pnl
                })

    if not completed_trades:
        print("警告：未能从日志中解析出任何完整的交易。")
        return pd.DataFrame()

    return pd.DataFrame(completed_trades)


def analyze_and_visualize(df):
    """
    分析波动率与盈亏的关系并进行可视化。

    Args:
        df (pandas.DataFrame): 包含'volatility'和'pnl'的数据框。
    """
    if df.empty:
        return

    # 1. 描述性统计
    print("--- 描述性统计 ---")
    print(df.describe())

    # 2. 计算相关系数
    correlation = df['volatility'].corr(df['pnl'])
    print(f"\n波动率与盈亏的相关系数为: {correlation:.4f}")
    if abs(correlation) < 0.2:
        print("相关系数绝对值小于0.2，表明波动率与盈亏之间几乎没有线性关系。")
    elif abs(correlation) < 0.5:
        print("相关系数在0.2到0.5之间，表明存在弱的线性关系。")
    else:
        print("相关系数大于0.5，表明存在较强的线性关系。")

    # 3. 可视化分析

    # 创建一个图形，包含两个子图
    fig, axes = plt.subplots(2, 2, figsize=(16, 12))
    fig.suptitle('波动率与仓位盈利能力关系分析', fontsize=20)

    # 子图1: 波动率与盈亏的散点图
    sns.scatterplot(data=df, x='volatility', y='pnl', ax=axes[0, 0], alpha=0.6)
    axes[0, 0].set_title('波动率 vs. 盈亏 (散点图)')
    axes[0, 0].set_xlabel('开仓时波动率')
    axes[0, 0].set_ylabel('单笔盈亏 (PNL)')
    axes[0, 0].grid(True)

    # 子图2: 添加回归线以观察趋势
    sns.regplot(data=df, x='volatility', y='pnl', ax=axes[0, 1],
                scatter_kws={'alpha': 0.5}, line_kws={"color": "red"})
    axes[0, 1].set_title('波动率 vs. 盈亏 (带回归线)')
    axes[0, 1].set_xlabel('开仓时波动率')
    axes[0, 1].set_ylabel('单笔盈亏 (PNL)')
    axes[0, 1].grid(True)

    # 准备数据用于分类比较
    df['盈利情况'] = np.where(df['pnl'] > 0, '盈利', '亏损')

    # 子图3: 盈利和亏损交易的波动率分布 (箱形图)
    sns.boxplot(data=df, x='盈利情况', y='volatility', ax=axes[1, 0])
    axes[1, 0].set_title('盈利与亏损交易的波动率分布')
    axes[1, 0].set_xlabel('交易结果')
    axes[1, 0].set_ylabel('开仓时波动率')

    # 子图4: 盈利和亏损交易的波动率分布 (小提琴图)
    sns.violinplot(data=df, x='盈利情况', y='volatility', ax=axes[1, 1])
    axes[1, 1].set_title('盈利与亏损交易的波动率分布 (更详细)')
    axes[1, 1].set_xlabel('交易结果')
    axes[1, 1].set_ylabel('开仓时波动率')

    plt.tight_layout(rect=[0, 0.03, 1, 0.96])
    plt.show()


# --- 主程序 ---
if __name__ == "__main__":
    file_path = '回测结果 (2).txt'
    trades_df = parse_backtest_log(file_path)

    if trades_df is not None:
        analyze_and_visualize(trades_df)